/* Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "common.h"
#include "spdm_command_impl.h"
#include "certificate/certificate_collection.h"

CertificateCollection::CertificateCollection()
{
    collection = new Certificate[MAX_SLOT_NUM];
}

CertificateCollection::~CertificateCollection()
{
    delete[] collection;
}

int32_t CertificateCollection::GetDigests(void *spdm_context, uint8_t *slot_diff_mask)
{
    uint8_t total_digest[MAX_SLOT_NUM * LIBSPDM_MAX_HASH_SIZE] = {0};
    uint8_t per_digest[LIBSPDM_MAX_HASH_SIZE] = {0};
    uint8_t slot_mask;

    int32_t ret = GetDigest(spdm_context, NULL, &slot_mask, total_digest);
    if (ret != LIBSPDM_STATUS_SUCCESS) {
        debug_log(DLOG_ERROR, "get digests failed");
        return RET_ERR;
    }

    // 当slot_mask为0时，说明8个槽位都没有证书，也就没有对应的digest
    if (slot_mask == 0) {
        debug_log(DLOG_ERROR, "there is no certificate digest");
        return RET_ERR;
    }

    *slot_diff_mask = 0;
    for (uint8_t slot_id = 0; slot_id < MAX_SLOT_NUM; slot_id++) {
        if (slot_mask && (1 << slot_id) == 0) {
            // 某一位为0，代表没证书了
            break;
        }

        memcpy_s(per_digest, LIBSPDM_MAX_HASH_SIZE, total_digest + slot_id * LIBSPDM_MAX_HASH_SIZE,
            LIBSPDM_MAX_HASH_SIZE);

        // 当获得的digest和证书已有的digest不匹配时，留标记记录，用于后续GET_CERTIFICATE
        if (memcmp(collection[slot_id].digest, per_digest, LIBSPDM_MAX_HASH_SIZE) != 0) {
            *slot_diff_mask |= (1 << slot_id);
        }

        // 有证书时，将对应的digest拷贝到证书资源下
        memcpy_s(collection[slot_id].digest, LIBSPDM_MAX_HASH_SIZE, per_digest, LIBSPDM_MAX_HASH_SIZE);
    }

    return RET_OK;
}

int32_t CertificateCollection::GetCertificate(void *spdm_context, uint8_t slot_id)
{
    if (slot_id >= MAX_SLOT_NUM) {
        debug_log(DLOG_ERROR, "invalid slot_id %u", slot_id);
        return RET_ERR;
    }

    vector<uint8_t> cert_chain(LIBSPDM_MAX_CERT_CHAIN_SIZE);
    size_t cert_chain_size = cert_chain.size();

    uint32_t status = ::GetCertificate(spdm_context, NULL, slot_id, &cert_chain_size, cert_chain.data());
    if (status != LIBSPDM_STATUS_SUCCESS) {
        debug_log(DLOG_ERROR, "get %u certificate failed, status: %u", slot_id, status);
        return RET_ERR;
    }

    size_t hash_size =
        GetHashSize(reinterpret_cast<libspdm_context_t *>(spdm_context)->connection_info.algorithm.base_hash_algo);

    // 2 bytes Length + 2 bytes Reserved
    constexpr size_t cert_chain_header_size = 4;

    if (cert_chain.size() < hash_size + cert_chain_header_size) {
        debug_log(DLOG_ERROR, "invalid cert chain length %u, needed %u",
            cert_chain.size(), hash_size + cert_chain_header_size);
        return RET_ERR;
    }

    // 选取出信息文本中的二进制串
    vector<uint8_t> der_cert_chain(cert_chain.begin() + cert_chain_header_size + hash_size, cert_chain.end());

    // 刷新证书对象
    Certificate& cert = collection[slot_id];
    cert.InitCertificateInfo(der_cert_chain);

    return RET_OK;
}